home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / tcl / tkstep0.3b3 / tkstep0 / tkstep / tkMenubutton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-08  |  39.4 KB  |  1,248 lines

  1. /* 
  2.  * tkMenubutton.c --
  3.  *
  4.  *    This module implements button-like widgets that are used
  5.  *    to invoke pull-down menus.
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tkMenubutton.c 1.77 96/02/15 18:52:22
  14.  */
  15. /*
  16.  * TkSTEP modifications Copyright
  17.  * by Alfredo K. Kojima 
  18.  */ 
  19. #include "tkPort.h"
  20. #include "default.h"
  21. #include "tkInt.h"
  22.  
  23. /*
  24.  * A data structure of the following type is kept for each
  25.  * widget managed by this file:
  26.  */
  27.  
  28. typedef struct {
  29.     Tk_Window tkwin;        /* Window that embodies the widget.  NULL
  30.                  * means that the window has been destroyed
  31.                  * but the data structures haven't yet been
  32.                  * cleaned up.*/
  33.     Display *display;        /* Display containing widget.  Needed, among
  34.                  * other things, so that resources can bee
  35.                  * freed up even after tkwin has gone away. */
  36.     Tcl_Interp *interp;        /* Interpreter associated with menubutton. */
  37.     Tcl_Command widgetCmd;    /* Token for menubutton's widget command. */
  38.     char *menuName;        /* Name of menu associated with widget.
  39.                  * Malloc-ed. */
  40.  
  41.     /*
  42.      * Information about what's displayed in the menu button:
  43.      */
  44.  
  45.     char *text;            /* Text to display in button (malloc'ed)
  46.                  * or NULL. */
  47.     int numChars;        /* # of characters in text. */
  48.     int underline;        /* Index of character to underline. */
  49.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  50.                  * If non-NULL, button displays the contents
  51.                  * of this variable. */
  52.     Pixmap bitmap;        /* Bitmap to display or None.  If not None
  53.                  * then text and textVar and underline
  54.                  * are ignored. */
  55.     char *imageString;        /* Name of image to display (malloc'ed), or
  56.                  * NULL.  If non-NULL, bitmap, text, and
  57.                  * textVarName are ignored. */
  58.     Tk_Image image;        /* Image to display in window, or NULL if
  59.                  * none. */
  60.  
  61.     /*
  62.      * Information used when displaying widget:
  63.      */
  64.  
  65.     Tk_Uid state;        /* State of button for display purposes:
  66.                  * normal, active, or disabled. */
  67.     Tk_3DBorder normalBorder;    /* Structure used to draw 3-D
  68.                  * border and background when window
  69.                  * isn't active.  NULL means no such
  70.                  * border exists. */
  71.     Tk_3DBorder activeBorder;    /* Structure used to draw 3-D
  72.                  * border and background when window
  73.                  * is active.  NULL means no such
  74.                  * border exists. */
  75.     int borderWidth;        /* Width of border. */
  76.     int relief;            /* 3-d effect: TK_RELIEF_RAISED, etc. */
  77.     int highlightWidth;        /* Width in pixels of highlight to draw
  78.                  * around widget when it has the focus.
  79.                  * <= 0 means don't draw a highlight. */
  80.     XColor *highlightBgColorPtr;
  81.                 /* Color for drawing traversal highlight
  82.                  * area when highlight is off. */
  83.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  84.     int inset;            /* Total width of all borders, including
  85.                  * traversal highlight and 3-D border.
  86.                  * Indicates how much interior stuff must
  87.                  * be offset from outside edges to leave
  88.                  * room for borders. */
  89.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  90.     XColor *normalFg;        /* Foreground color in normal mode. */
  91.     XColor *activeFg;        /* Foreground color in active mode.  NULL
  92.                  * means use normalFg instead. */
  93.     XColor *disabledFg;        /* Foreground color when disabled.  NULL
  94.                  * means use normalFg with a 50% stipple
  95.                  * instead. */
  96.     GC normalTextGC;        /* GC for drawing text in normal mode. */
  97.     GC activeTextGC;        /* GC for drawing text in active mode (NULL
  98.                  * means use normalTextGC). */
  99.     Pixmap gray;        /* Pixmap for displaying disabled text/icon if
  100.                  * disabledFg is NULL. */
  101.     GC disabledGC;        /* Used to produce disabled effect.  If
  102.                  * disabledFg isn't NULL, this GC is used to
  103.                  * draw button text or icon.  Otherwise
  104.                  * text or icon is drawn with normalGC and
  105.                  * this GC is used to stipple background
  106.                  * across it. */
  107.     int leftBearing;        /* Distance from text origin to leftmost drawn
  108.                  * pixel (positive means to right). */
  109.     int rightBearing;        /* Amount text sticks right from its origin. */
  110.     char *widthString;        /* Value of -width option.  Malloc'ed. */
  111.     char *heightString;        /* Value of -height option.  Malloc'ed. */
  112.     int width, height;        /* If > 0, these specify dimensions to request
  113.                  * for window, in characters for text and in
  114.                  * pixels for bitmaps.  In this case the actual
  115.                  * size of the text string or bitmap is
  116.                  * ignored in computing desired window size. */
  117.     int wrapLength;        /* Line length (in pixels) at which to wrap
  118.                  * onto next line.  <= 0 means don't wrap
  119.                  * except at newlines. */
  120.     int padX, padY;        /* Extra space around text or bitmap (pixels
  121.                  * on each side). */
  122.     Tk_Anchor anchor;        /* Where text/bitmap should be displayed
  123.                  * inside window region. */
  124.     Tk_Justify justify;        /* Justification to use for multi-line text. */
  125.     int textWidth;        /* Width needed to display text as requested,
  126.                  * in pixels. */
  127.     int textHeight;        /* Height needed to display text as requested,
  128.                  * in pixels. */
  129.     int indicatorOn;        /* Non-zero means display indicator;  0 means
  130.                  * don't display. */
  131.     int indicatorHeight;    /* Height of indicator in pixels.  This same
  132.                  * amount of extra space is also left on each
  133.                  * side of the indicator. 0 if no indicator. */
  134.     int indicatorWidth;        /* Width of indicator in pixels, including
  135.                  * indicatorHeight in padding on each side.
  136.                  * 0 if no indicator. */
  137.  
  138.     /*
  139.      * Miscellaneous information:
  140.      */
  141.  
  142.     Tk_Cursor cursor;        /* Current cursor for window, or None. */
  143.     char *takeFocus;        /* Value of -takefocus option;  not used in
  144.                  * the C code, but used by keyboard traversal
  145.                  * scripts.  Malloc'ed, but may be NULL. */
  146.     int flags;            /* Various flags;  see below for
  147.                  * definitions. */
  148. } MenuButton;
  149.  
  150. /*
  151.  * Flag bits for buttons:
  152.  *
  153.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  154.  *                has already been queued to redraw
  155.  *                this window.
  156.  * POSTED:            Non-zero means that the menu associated
  157.  *                with this button has been posted (typically
  158.  *                because of an active button press).
  159.  * GOT_FOCUS:            Non-zero means this button currently
  160.  *                has the input focus.
  161.  */
  162.  
  163. #define REDRAW_PENDING        1
  164. #define POSTED            2
  165. #define GOT_FOCUS        4
  166.  
  167. /*
  168.  * The following constants define the dimensions of the cascade indicator,
  169.  * which is displayed if the "-indicatoron" option is true.  The units for
  170.  * these options are 1/10 millimeters.
  171.  */
  172.  
  173. #define INDICATOR_WIDTH        35
  174. #define INDICATOR_HEIGHT    22
  175.  
  176. /*
  177.  * Information used for parsing configuration specs:
  178.  */
  179.  
  180. static Tk_ConfigSpec configSpecs[] = {
  181.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  182.     DEF_MENUBUTTON_ACTIVE_BG_COLOR, Tk_Offset(MenuButton, activeBorder),
  183.     TK_CONFIG_COLOR_ONLY},
  184.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  185.     DEF_MENUBUTTON_ACTIVE_BG_MONO, Tk_Offset(MenuButton, activeBorder),
  186.     TK_CONFIG_MONO_ONLY},
  187.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  188.     DEF_MENUBUTTON_ACTIVE_FG_COLOR, Tk_Offset(MenuButton, activeFg),
  189.     TK_CONFIG_COLOR_ONLY},
  190.     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
  191.     DEF_MENUBUTTON_ACTIVE_FG_MONO, Tk_Offset(MenuButton, activeFg),
  192.     TK_CONFIG_MONO_ONLY},
  193.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  194.     DEF_MENUBUTTON_ANCHOR, Tk_Offset(MenuButton, anchor), 0},
  195.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  196.     DEF_MENUBUTTON_BG_COLOR, Tk_Offset(MenuButton, normalBorder),
  197.     TK_CONFIG_COLOR_ONLY},
  198.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  199.     DEF_MENUBUTTON_BG_MONO, Tk_Offset(MenuButton, normalBorder),
  200.     TK_CONFIG_MONO_ONLY},
  201.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  202.     (char *) NULL, 0, 0},
  203.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  204.     (char *) NULL, 0, 0},
  205.     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
  206.     DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap),
  207.     TK_CONFIG_NULL_OK},
  208.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  209.     DEF_MENUBUTTON_BORDER_WIDTH, Tk_Offset(MenuButton, borderWidth), 0},
  210.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  211.     DEF_MENUBUTTON_CURSOR, Tk_Offset(MenuButton, cursor),
  212.     TK_CONFIG_NULL_OK},
  213.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  214.     "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR,
  215.     Tk_Offset(MenuButton, disabledFg),
  216.     TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
  217.     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
  218.     "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_MONO,
  219.     Tk_Offset(MenuButton, disabledFg),
  220.     TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
  221.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  222.     (char *) NULL, 0, 0},
  223.     {TK_CONFIG_FONT, "-font", "font", "Font",
  224.     DEF_MENUBUTTON_FONT, Tk_Offset(MenuButton, fontPtr), 0},
  225.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  226.     DEF_MENUBUTTON_FG, Tk_Offset(MenuButton, normalFg), 0},
  227.     {TK_CONFIG_STRING, "-height", "height", "Height",
  228.     DEF_MENUBUTTON_HEIGHT, Tk_Offset(MenuButton, heightString), 0},
  229.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  230.     "HighlightBackground", DEF_MENUBUTTON_HIGHLIGHT_BG,
  231.     Tk_Offset(MenuButton, highlightBgColorPtr), 0},
  232.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  233.     DEF_MENUBUTTON_HIGHLIGHT, Tk_Offset(MenuButton, highlightColorPtr), 0},
  234.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  235.     "HighlightThickness", DEF_MENUBUTTON_HIGHLIGHT_WIDTH,
  236.     Tk_Offset(MenuButton, highlightWidth), 0},
  237.     {TK_CONFIG_STRING, "-image", "image", "Image",
  238.     DEF_MENUBUTTON_IMAGE, Tk_Offset(MenuButton, imageString),
  239.     TK_CONFIG_NULL_OK},
  240.     {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
  241.     DEF_MENUBUTTON_INDICATOR, Tk_Offset(MenuButton, indicatorOn), 0},
  242.     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
  243.     DEF_MENUBUTTON_JUSTIFY, Tk_Offset(MenuButton, justify), 0},
  244.     {TK_CONFIG_STRING, "-menu", "menu", "Menu",
  245.     DEF_MENUBUTTON_MENU, Tk_Offset(MenuButton, menuName),
  246.     TK_CONFIG_NULL_OK},
  247.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  248.     DEF_MENUBUTTON_PADX, Tk_Offset(MenuButton, padX), 0},
  249.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  250.     DEF_MENUBUTTON_PADY, Tk_Offset(MenuButton, padY), 0},
  251.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  252.     DEF_MENUBUTTON_RELIEF, Tk_Offset(MenuButton, relief), 0},
  253.     {TK_CONFIG_UID, "-state", "state", "State",
  254.     DEF_MENUBUTTON_STATE, Tk_Offset(MenuButton, state), 0},
  255.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  256.     DEF_MENUBUTTON_TAKE_FOCUS, Tk_Offset(MenuButton, takeFocus),
  257.     TK_CONFIG_NULL_OK},
  258.     {TK_CONFIG_STRING, "-text", "text", "Text",
  259.     DEF_MENUBUTTON_TEXT, Tk_Offset(MenuButton, text), 0},
  260.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  261.     DEF_MENUBUTTON_TEXT_VARIABLE, Tk_Offset(MenuButton, textVarName),
  262.     TK_CONFIG_NULL_OK},
  263.     {TK_CONFIG_INT, "-underline", "underline", "Underline",
  264.     DEF_MENUBUTTON_UNDERLINE, Tk_Offset(MenuButton, underline), 0},
  265.     {TK_CONFIG_STRING, "-width", "width", "Width",
  266.     DEF_MENUBUTTON_WIDTH, Tk_Offset(MenuButton, widthString), 0},
  267.     {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
  268.     DEF_MENUBUTTON_WRAP_LENGTH, Tk_Offset(MenuButton, wrapLength), 0},
  269.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  270.     (char *) NULL, 0, 0}
  271. };
  272.  
  273. /*
  274.  * Forward declarations for procedures defined later in this file:
  275.  */
  276.  
  277. static void        ComputeMenuButtonGeometry _ANSI_ARGS_((
  278.                 MenuButton *mbPtr));
  279. static void        MenuButtonCmdDeletedProc _ANSI_ARGS_((
  280.                 ClientData clientData));
  281. static void        MenuButtonEventProc _ANSI_ARGS_((ClientData clientData,
  282.                 XEvent *eventPtr));
  283. static void        MenuButtonImageProc _ANSI_ARGS_((ClientData clientData,
  284.                 int x, int y, int width, int height, int imgWidth,
  285.                 int imgHeight));
  286. static char *        MenuButtonTextVarProc _ANSI_ARGS_((
  287.                 ClientData clientData, Tcl_Interp *interp,
  288.                 char *name1, char *name2, int flags));
  289. static int        MenuButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
  290.                 Tcl_Interp *interp, int argc, char **argv));
  291. static int        ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp,
  292.                 MenuButton *mbPtr, int argc, char **argv,
  293.                 int flags));
  294. static void        DestroyMenuButton _ANSI_ARGS_((char *memPtr));
  295. static void        DisplayMenuButton _ANSI_ARGS_((ClientData clientData));
  296.  
  297. /*
  298.  *--------------------------------------------------------------
  299.  *
  300.  * Tk_MenubuttonCmd --
  301.  *
  302.  *    This procedure is invoked to process the "button", "label",
  303.  *    "radiobutton", and "checkbutton" Tcl commands.  See the
  304.  *    user documentation for details on what it does.
  305.  *
  306.  * Results:
  307.  *    A standard Tcl result.
  308.  *
  309.  * Side effects:
  310.  *    See the user documentation.
  311.  *
  312.  *--------------------------------------------------------------
  313.  */
  314.  
  315. int
  316. Tk_MenubuttonCmd(clientData, interp, argc, argv)
  317.     ClientData clientData;    /* Main window associated with
  318.                  * interpreter. */
  319.     Tcl_Interp *interp;        /* Current interpreter. */
  320.     int argc;            /* Number of arguments. */
  321.     char **argv;        /* Argument strings. */
  322. {
  323.     register MenuButton *mbPtr;
  324.     Tk_Window tkwin = (Tk_Window) clientData;
  325.     Tk_Window new;
  326.  
  327.     if (argc < 2) {
  328.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  329.         argv[0], " pathName ?options?\"", (char *) NULL);
  330.     return TCL_ERROR;
  331.     }
  332.  
  333.     /*
  334.      * Create the new window.
  335.      */
  336.  
  337.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  338.     if (new == NULL) {
  339.     return TCL_ERROR;
  340.     }
  341.  
  342.     /*
  343.      * Initialize the data structure for the button.
  344.      */
  345.  
  346.     mbPtr = (MenuButton *) ckalloc(sizeof(MenuButton));
  347.     mbPtr->tkwin = new;
  348.     mbPtr->display = Tk_Display (new);
  349.     mbPtr->interp = interp;
  350.     mbPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(mbPtr->tkwin),
  351.         MenuButtonWidgetCmd, (ClientData) mbPtr, MenuButtonCmdDeletedProc);
  352.     mbPtr->menuName = NULL;
  353.     mbPtr->text = NULL;
  354.     mbPtr->numChars = 0;
  355.     mbPtr->underline = -1;
  356.     mbPtr->textVarName = NULL;
  357.     mbPtr->bitmap = None;
  358.     mbPtr->imageString = NULL;
  359.     mbPtr->image = NULL;
  360.     mbPtr->state = tkNormalUid;
  361.     mbPtr->normalBorder = NULL;
  362.     mbPtr->activeBorder = NULL;
  363.     mbPtr->borderWidth = 0;
  364.     mbPtr->relief = TK_RELIEF_FLAT;
  365.     mbPtr->highlightWidth = 0;
  366.     mbPtr->highlightBgColorPtr = NULL;
  367.     mbPtr->highlightColorPtr = NULL;
  368.     mbPtr->inset = 0;
  369.     mbPtr->fontPtr = NULL;
  370.     mbPtr->normalFg = NULL;
  371.     mbPtr->activeFg = NULL;
  372.     mbPtr->disabledFg = NULL;
  373.     mbPtr->normalTextGC = None;
  374.     mbPtr->activeTextGC = None;
  375.     mbPtr->gray = None;
  376.     mbPtr->disabledGC = None;
  377.     mbPtr->leftBearing = 0;
  378.     mbPtr->rightBearing = 0;
  379.     mbPtr->widthString = NULL;
  380.     mbPtr->heightString = NULL;
  381.     mbPtr->width = 0;
  382.     mbPtr->width = 0;
  383.     mbPtr->wrapLength = 0;
  384.     mbPtr->padX = 0;
  385.     mbPtr->padY = 0;
  386.     mbPtr->anchor = TK_ANCHOR_CENTER;
  387.     mbPtr->justify = TK_JUSTIFY_CENTER;
  388.     mbPtr->indicatorOn = 0;
  389.     mbPtr->indicatorWidth = 0;
  390.     mbPtr->indicatorHeight = 0;
  391.     mbPtr->cursor = None;
  392.     mbPtr->takeFocus = NULL;
  393.     mbPtr->flags = 0;
  394.  
  395.     Tk_SetClass(mbPtr->tkwin, "Menubutton");
  396.     Tk_CreateEventHandler(mbPtr->tkwin,
  397.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  398.         MenuButtonEventProc, (ClientData) mbPtr);
  399.     if (ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, 0) != TCL_OK) {
  400.     Tk_DestroyWindow(mbPtr->tkwin);
  401.     return TCL_ERROR;
  402.     }
  403.  
  404.     interp->result = Tk_PathName(mbPtr->tkwin);
  405.     return TCL_OK;
  406. }
  407.  
  408. /*
  409.  *--------------------------------------------------------------
  410.  *
  411.  * MenuButtonWidgetCmd --
  412.  *
  413.  *    This procedure is invoked to process the Tcl command
  414.  *    that corresponds to a widget managed by this module.
  415.  *    See the user documentation for details on what it does.
  416.  *
  417.  * Results:
  418.  *    A standard Tcl result.
  419.  *
  420.  * Side effects:
  421.  *    See the user documentation.
  422.  *
  423.  *--------------------------------------------------------------
  424.  */
  425.  
  426. static int
  427. MenuButtonWidgetCmd(clientData, interp, argc, argv)
  428.     ClientData clientData;    /* Information about button widget. */
  429.     Tcl_Interp *interp;        /* Current interpreter. */
  430.     int argc;            /* Number of arguments. */
  431.     char **argv;        /* Argument strings. */
  432. {
  433.     register MenuButton *mbPtr = (MenuButton *) clientData;
  434.     int result = TCL_OK;
  435.     size_t length;
  436.     int c;
  437.  
  438.     if (argc < 2) {
  439.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  440.         " option ?arg arg ...?\"", (char *) NULL);
  441.     return TCL_ERROR;
  442.     }
  443.     Tcl_Preserve((ClientData) mbPtr);
  444.     c = argv[1][0];
  445.     length = strlen(argv[1]);
  446.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  447.         && (length >= 2)) {
  448.     if (argc != 3) {
  449.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  450.             argv[0], " cget option\"",
  451.             (char *) NULL);
  452.         goto error;
  453.     }
  454.     result = Tk_ConfigureValue(interp, mbPtr->tkwin, configSpecs,
  455.         (char *) mbPtr, argv[2], 0);
  456.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  457.         && (length >= 2)) {
  458.     if (argc == 2) {
  459.         result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
  460.             (char *) mbPtr, (char *) NULL, 0);
  461.     } else if (argc == 3) {
  462.         result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
  463.             (char *) mbPtr, argv[2], 0);
  464.     } else {
  465.         result = ConfigureMenuButton(interp, mbPtr, argc-2, argv+2,
  466.             TK_CONFIG_ARGV_ONLY);
  467.     }
  468.     } else {
  469.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  470.         "\": must be cget or configure",
  471.         (char *) NULL);
  472.     goto error;
  473.     }
  474.     Tcl_Release((ClientData) mbPtr);
  475.     return result;
  476.  
  477.     error:
  478.     Tcl_Release((ClientData) mbPtr);
  479.     return TCL_ERROR;
  480. }
  481.  
  482. /*
  483.  *----------------------------------------------------------------------
  484.  *
  485.  * DestroyMenuButton --
  486.  *
  487.  *    This procedure is invoked to recycle all of the resources
  488.  *    associated with a button widget.  It is invoked as a
  489.  *    when-idle handler in order to make sure that there is no
  490.  *    other use of the button pending at the time of the deletion.
  491.  *
  492.  * Results:
  493.  *    None.
  494.  *
  495.  * Side effects:
  496.  *    Everything associated with the widget is freed up.
  497.  *
  498.  *----------------------------------------------------------------------
  499.  */
  500.  
  501. static void
  502. DestroyMenuButton(memPtr)
  503.     char *memPtr;        /* Info about button widget. */
  504. {
  505.     register MenuButton *mbPtr = (MenuButton *) memPtr;
  506.  
  507.     /*
  508.      * Free up all the stuff that requires special handling, then
  509.      * let Tk_FreeOptions handle all the standard option-related
  510.      * stuff.
  511.      */
  512.  
  513.     if (mbPtr->textVarName != NULL) {
  514.     Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName,
  515.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  516.         MenuButtonTextVarProc, (ClientData) mbPtr);
  517.     }
  518.     if (mbPtr->image != NULL) {
  519.     Tk_FreeImage(mbPtr->image);
  520.     }
  521.     if (mbPtr->normalTextGC != None) {
  522.     Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
  523.     }
  524.     if (mbPtr->activeTextGC != None) {
  525.     Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
  526.     }
  527.     if (mbPtr->gray != None) {
  528.     Tk_FreeBitmap(mbPtr->display, mbPtr->gray);
  529.     }
  530.     if (mbPtr->disabledGC != None) {
  531.     Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
  532.     }
  533.     Tk_FreeOptions(configSpecs, (char *) mbPtr, mbPtr->display, 0);
  534.     ckfree((char *) mbPtr);
  535. }
  536.  
  537. /*
  538.  *----------------------------------------------------------------------
  539.  *
  540.  * ConfigureMenuButton --
  541.  *
  542.  *    This procedure is called to process an argv/argc list, plus
  543.  *    the Tk option database, in order to configure (or
  544.  *    reconfigure) a menubutton widget.
  545.  *
  546.  * Results:
  547.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  548.  *    returned, then interp->result contains an error message.
  549.  *
  550.  * Side effects:
  551.  *    Configuration information, such as text string, colors, font,
  552.  *    etc. get set for mbPtr;  old resources get freed, if there
  553.  *    were any.  The menubutton is redisplayed.
  554.  *
  555.  *----------------------------------------------------------------------
  556.  */
  557.  
  558. static int
  559. ConfigureMenuButton(interp, mbPtr, argc, argv, flags)
  560.     Tcl_Interp *interp;        /* Used for error reporting. */
  561.     register MenuButton *mbPtr;    /* Information about widget;  may or may
  562.                  * not already have values for some fields. */
  563.     int argc;            /* Number of valid entries in argv. */
  564.     char **argv;        /* Arguments. */
  565.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  566. {
  567.     XGCValues gcValues;
  568.     GC newGC;
  569.     unsigned long mask;
  570.     int result;
  571.     Tk_Image image;
  572.  
  573.     /*
  574.      * Eliminate any existing trace on variables monitored by the menubutton.
  575.      */
  576.  
  577.     if (mbPtr->textVarName != NULL) {
  578.     Tcl_UntraceVar(interp, mbPtr->textVarName,
  579.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  580.         MenuButtonTextVarProc, (ClientData) mbPtr);
  581.     }
  582.  
  583.     result = Tk_ConfigureWidget(interp, mbPtr->tkwin, configSpecs,
  584.         argc, argv, (char *) mbPtr, flags);
  585.     if (result != TCL_OK) {
  586.     return TCL_ERROR;
  587.     }
  588.  
  589.     /*
  590.      * A few options need special processing, such as setting the
  591.      * background from a 3-D border, or filling in complicated
  592.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  593.      */
  594.  
  595.     if ((mbPtr->state == tkActiveUid) && !Tk_StrictMotif(mbPtr->tkwin)) {
  596.     Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
  597.     } else {
  598.     Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
  599.     if ((mbPtr->state != tkNormalUid) && (mbPtr->state != tkActiveUid)
  600.         && (mbPtr->state != tkDisabledUid)) {
  601.         Tcl_AppendResult(interp, "bad state value \"", mbPtr->state,
  602.             "\": must be normal, active, or disabled", (char *) NULL);
  603.         mbPtr->state = tkNormalUid;
  604.         return TCL_ERROR;
  605.     }
  606.     }
  607.  
  608.     if (mbPtr->highlightWidth < 0) {
  609.     mbPtr->highlightWidth = 0;
  610.     }
  611.  
  612.     gcValues.font = mbPtr->fontPtr->fid;
  613.     gcValues.foreground = mbPtr->normalFg->pixel;
  614.     gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
  615.  
  616.     /*
  617.      * Note: GraphicsExpose events are disabled in GC's because they're
  618.      * used to copy stuff from an off-screen pixmap onto the screen (we know
  619.      * that there's no problem with obscured areas).
  620.      */
  621.  
  622.     gcValues.graphics_exposures = False;
  623.     newGC = Tk_GetGC(mbPtr->tkwin,
  624.         GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
  625.     if (mbPtr->normalTextGC != None) {
  626.     Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
  627.     }
  628.     mbPtr->normalTextGC = newGC;
  629.  
  630.     gcValues.font = mbPtr->fontPtr->fid;
  631.     gcValues.foreground = mbPtr->activeFg->pixel;
  632.     gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel;
  633.     newGC = Tk_GetGC(mbPtr->tkwin, GCForeground|GCBackground|GCFont,
  634.         &gcValues);
  635.     if (mbPtr->activeTextGC != None) {
  636.     Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
  637.     }
  638.     mbPtr->activeTextGC = newGC;
  639.  
  640.     gcValues.font = mbPtr->fontPtr->fid;
  641.     gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
  642.     if ((mbPtr->disabledFg != NULL) && (mbPtr->imageString == NULL)) {
  643.     gcValues.foreground = mbPtr->disabledFg->pixel;
  644.     mask = GCForeground|GCBackground|GCFont;
  645.     } else {
  646.     gcValues.foreground = gcValues.background;
  647.     if (mbPtr->gray == None) {
  648.         mbPtr->gray = Tk_GetBitmap(interp, mbPtr->tkwin,
  649.             Tk_GetUid("gray50"));
  650.         if (mbPtr->gray == None) {
  651.         return TCL_ERROR;
  652.         }
  653.     }
  654.     gcValues.fill_style = FillStippled;
  655.     gcValues.stipple = mbPtr->gray;
  656.     mask = GCForeground|GCFillStyle|GCStipple;
  657.     }
  658.     newGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
  659.     if (mbPtr->disabledGC != None) {
  660.     Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
  661.     }
  662.     mbPtr->disabledGC = newGC;
  663.  
  664.     if (mbPtr->padX < 0) {
  665.     mbPtr->padX = 0;
  666.     }
  667.     if (mbPtr->padY < 0) {
  668.     mbPtr->padY = 0;
  669.     }
  670.  
  671.     /*
  672.      * Get the image for the widget, if there is one.  Allocate the
  673.      * new image before freeing the old one, so that the reference
  674.      * count doesn't go to zero and cause image data to be discarded.
  675.      */
  676.  
  677.     if (mbPtr->imageString != NULL) {
  678.     image = Tk_GetImage(mbPtr->interp, mbPtr->tkwin,
  679.         mbPtr->imageString, MenuButtonImageProc, (ClientData) mbPtr);
  680.     if (image == NULL) {
  681.         return TCL_ERROR;
  682.     }
  683.     } else {
  684.     image = NULL;
  685.     }
  686.     if (mbPtr->image != NULL) {
  687.     Tk_FreeImage(mbPtr->image);
  688.     }
  689.     mbPtr->image = image;
  690.  
  691.     if ((mbPtr->image == NULL) && (mbPtr->bitmap == None)
  692.         && (mbPtr->textVarName != NULL)) {
  693.     /*
  694.      * The menubutton displays a variable.  Set up a trace to watch
  695.      * for any changes in it.
  696.      */
  697.  
  698.     char *value;
  699.  
  700.     value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
  701.     if (value == NULL) {
  702.         Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
  703.             TCL_GLOBAL_ONLY);
  704.     } else {
  705.         if (mbPtr->text != NULL) {
  706.         ckfree(mbPtr->text);
  707.         }
  708.         mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  709.         strcpy(mbPtr->text, value);
  710.     }
  711.     Tcl_TraceVar(interp, mbPtr->textVarName,
  712.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  713.         MenuButtonTextVarProc, (ClientData) mbPtr);
  714.     }
  715.  
  716.     /*
  717.      * Recompute the geometry for the button.
  718.      */
  719.  
  720.     if ((mbPtr->bitmap != None) || (mbPtr->image != NULL)) {
  721.     if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->widthString,
  722.         &mbPtr->width) != TCL_OK) {
  723.         widthError:
  724.         Tcl_AddErrorInfo(interp, "\n    (processing -width option)");
  725.         return TCL_ERROR;
  726.     }
  727.     if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->heightString,
  728.         &mbPtr->height) != TCL_OK) {
  729.         heightError:
  730.         Tcl_AddErrorInfo(interp, "\n    (processing -height option)");
  731.         return TCL_ERROR;
  732.     }
  733.     } else {
  734.     if (Tcl_GetInt(interp, mbPtr->widthString, &mbPtr->width)
  735.         != TCL_OK) {
  736.         goto widthError;
  737.     }
  738.     if (Tcl_GetInt(interp, mbPtr->heightString, &mbPtr->height)
  739.         != TCL_OK) {
  740.         goto heightError;
  741.     }
  742.     }
  743.     ComputeMenuButtonGeometry(mbPtr);
  744.  
  745.     /*
  746.      * Lastly, arrange for the button to be redisplayed.
  747.      */
  748.  
  749.     if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
  750.     Tcl_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  751.     mbPtr->flags |= REDRAW_PENDING;
  752.     }
  753.  
  754.     return TCL_OK;
  755. }
  756.  
  757. /*
  758.  *----------------------------------------------------------------------
  759.  *
  760.  * DisplayMenuButton --
  761.  *
  762.  *    This procedure is invoked to display a menubutton widget.
  763.  *
  764.  * Results:
  765.  *    None.
  766.  *
  767.  * Side effects:
  768.  *    Commands are output to X to display the menubutton in its
  769.  *    current mode.
  770.  *
  771.  *----------------------------------------------------------------------
  772.  */
  773.  
  774. static void
  775. DisplayMenuButton(clientData)
  776.     ClientData clientData;    /* Information about widget. */
  777. {
  778.     register MenuButton *mbPtr = (MenuButton *) clientData;
  779.     GC gc;
  780.     Tk_3DBorder border;
  781.     Pixmap pixmap;
  782.     int x = 0;            /* Initialization needed only to stop
  783.                  * compiler warning. */
  784.     int y;
  785.     register Tk_Window tkwin = mbPtr->tkwin;
  786.     int width, height;
  787.  
  788.     mbPtr->flags &= ~REDRAW_PENDING;
  789.     if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  790.     return;
  791.     }
  792.  
  793.     if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg != NULL)) {
  794.     gc = mbPtr->disabledGC;
  795.     border = mbPtr->normalBorder;
  796.     } else if ((mbPtr->state == tkActiveUid) && !Tk_StrictMotif(mbPtr->tkwin)) {
  797.     gc = mbPtr->activeTextGC;
  798.     border = mbPtr->activeBorder;
  799.     } else {
  800.     gc = mbPtr->normalTextGC;
  801.     border = mbPtr->normalBorder;
  802.     }
  803.  
  804.     /*
  805.      * In order to avoid screen flashes, this procedure redraws
  806.      * the menu button in a pixmap, then copies the pixmap to the
  807.      * screen in a single operation.  This means that there's no
  808.      * point in time where the on-sreen image has been cleared.
  809.      */
  810.  
  811.     pixmap = Tk_GetPixmap(mbPtr->display, Tk_WindowId(tkwin),
  812.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  813.     Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
  814.         Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  815.  
  816.     /*
  817.      * Display image or bitmap or text for button.
  818.      */
  819.  
  820.     if (mbPtr->image != None) {
  821.     Tk_SizeOfImage(mbPtr->image, &width, &height);
  822.  
  823.     imageOrBitmap:
  824.     switch (mbPtr->anchor) {
  825.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  826.         x += mbPtr->inset;
  827.         break;
  828.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  829.         x += ((int) (Tk_Width(tkwin) - width
  830.             - mbPtr->indicatorWidth))/2;
  831.         break;
  832.         default:
  833.         x += Tk_Width(tkwin) - mbPtr->inset - width
  834.             - mbPtr->indicatorWidth;
  835.         break;
  836.     }
  837.     switch (mbPtr->anchor) {
  838.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  839.         y = mbPtr->inset;
  840.         break;
  841.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  842.         y = ((int) (Tk_Height(tkwin) - height))/2;
  843.         break;
  844.         default:
  845.         y = Tk_Height(tkwin) - mbPtr->inset - height;
  846.         break;
  847.     }
  848.     if (mbPtr->image != NULL) {
  849.         Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap,
  850.             x, y);
  851.     } else {
  852.         XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap,
  853.             gc, 0, 0, (unsigned) width, (unsigned) height, x, y, 1);
  854.     }
  855.     } else if (mbPtr->bitmap != None) {
  856.     Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
  857.     goto imageOrBitmap;
  858.     } else {
  859.     width = mbPtr->textWidth;
  860.     height = mbPtr->textHeight;
  861.     switch (mbPtr->anchor) {
  862.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  863.         x = mbPtr->inset + mbPtr->padX;
  864.         break;
  865.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  866.         x = ((int) (Tk_Width(tkwin) - width
  867.             - mbPtr->indicatorWidth))/2;
  868.         break;
  869.         default:
  870.         x = Tk_Width(tkwin) - width - mbPtr->padX - mbPtr->inset
  871.             - mbPtr->indicatorWidth;
  872.         break;
  873.     }
  874.     switch (mbPtr->anchor) {
  875.         case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  876.         y = mbPtr->inset + mbPtr->padY;
  877.         break;
  878.         case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  879.         y = ((int) (Tk_Height(tkwin) - height))/2;
  880.         break;
  881.         default:
  882.         y = Tk_Height(tkwin) - mbPtr->inset - mbPtr->padY - height;
  883.         break;
  884.     }
  885.     TkDisplayText(mbPtr->display, pixmap, mbPtr->fontPtr,
  886.         mbPtr->text, mbPtr->numChars, x, y, mbPtr->textWidth,
  887.         mbPtr->justify, mbPtr->underline, gc);
  888.     }
  889.  
  890.     /*
  891.      * If the menu button is disabled with a stipple rather than a special
  892.      * foreground color, generate the stippled effect.
  893.      */
  894.  
  895.     if ((mbPtr->state == tkDisabledUid)
  896.         && ((mbPtr->disabledFg == NULL) || (mbPtr->image != NULL))) {
  897.     XFillRectangle(mbPtr->display, pixmap, mbPtr->disabledGC,
  898.         mbPtr->inset, mbPtr->inset,
  899.         (unsigned) (Tk_Width(tkwin) - 2*mbPtr->inset),
  900.         (unsigned) (Tk_Height(tkwin) - 2*mbPtr->inset));
  901.     }
  902.  
  903.     /*
  904.      * Draw the cascade indicator for the menu button on the
  905.      * right side of the window, if desired.
  906.      */
  907.  
  908.     if (mbPtr->indicatorOn) {
  909.     int borderWidth;
  910.  
  911.     borderWidth = (mbPtr->indicatorHeight+1)/3;
  912.     if (borderWidth < 1) {
  913.         borderWidth = 1;
  914.     }
  915.     XFillRectangle(mbPtr->display, pixmap, 
  916.         Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC),
  917.         Tk_Width(tkwin) - mbPtr->inset - mbPtr->indicatorWidth
  918.             + mbPtr->indicatorHeight + 2,
  919.         y + ((int) (height - mbPtr->indicatorHeight))/2,
  920.         mbPtr->indicatorWidth - 2*mbPtr->indicatorHeight,
  921.         mbPtr->indicatorHeight);
  922.     Tk_Fill3DRectangle(tkwin, pixmap, border,
  923.         Tk_Width(tkwin) - mbPtr->inset - mbPtr->indicatorWidth
  924.             + mbPtr->indicatorHeight,
  925.         y + ((int) (height - mbPtr->indicatorHeight))/2 - 2,
  926.         mbPtr->indicatorWidth - 2*mbPtr->indicatorHeight,
  927.         mbPtr->indicatorHeight, borderWidth, TK_RELIEF_RAISED);
  928.     }
  929.  
  930.     /*
  931.      * Draw the border and traversal highlight last.  This way, if the
  932.      * menu button's contents overflow onto the border they'll be covered
  933.      * up by the border.
  934.      */
  935.  
  936.     if (mbPtr->relief != TK_RELIEF_FLAT) {
  937.     Tk_Draw3DRectangle(tkwin, pixmap, border,
  938.         mbPtr->highlightWidth, mbPtr->highlightWidth,
  939.         Tk_Width(tkwin) - 2*mbPtr->highlightWidth,
  940.         Tk_Height(tkwin) - 2*mbPtr->highlightWidth,
  941.         mbPtr->borderWidth, mbPtr->relief);
  942.     }
  943.     if (mbPtr->highlightWidth != 0) {
  944.     GC gc;
  945.  
  946.     if (mbPtr->flags & GOT_FOCUS) {
  947.         gc = Tk_GCForColor(mbPtr->highlightColorPtr, pixmap);
  948.     } else {
  949.         gc = Tk_GCForColor(mbPtr->highlightBgColorPtr, pixmap);
  950.     }
  951.     Tk_DrawFocusHighlight(tkwin, gc, mbPtr->highlightWidth, pixmap);
  952.     }
  953.  
  954.     /*
  955.      * Copy the information from the off-screen pixmap onto the screen,
  956.      * then delete the pixmap.
  957.      */
  958.  
  959.     XCopyArea(mbPtr->display, pixmap, Tk_WindowId(tkwin),
  960.         mbPtr->normalTextGC, 0, 0, (unsigned) Tk_Width(tkwin),
  961.         (unsigned) Tk_Height(tkwin), 0, 0);
  962.     Tk_FreePixmap(mbPtr->display, pixmap);
  963. }
  964.  
  965. /*
  966.  *--------------------------------------------------------------
  967.  *
  968.  * MenuButtonEventProc --
  969.  *
  970.  *    This procedure is invoked by the Tk dispatcher for various
  971.  *    events on buttons.
  972.  *
  973.  * Results:
  974.  *    None.
  975.  *
  976.  * Side effects:
  977.  *    When the window gets deleted, internal structures get
  978.  *    cleaned up.  When it gets exposed, it is redisplayed.
  979.  *
  980.  *--------------------------------------------------------------
  981.  */
  982.  
  983. static void
  984. MenuButtonEventProc(clientData, eventPtr)
  985.     ClientData clientData;    /* Information about window. */
  986.     XEvent *eventPtr;        /* Information about event. */
  987. {
  988.     MenuButton *mbPtr = (MenuButton *) clientData;
  989.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  990.     goto redraw;
  991.     } else if (eventPtr->type == ConfigureNotify) {
  992.     /*
  993.      * Must redraw after size changes, since layout could have changed
  994.      * and borders will need to be redrawn.
  995.      */
  996.  
  997.     goto redraw;
  998.     } else if (eventPtr->type == DestroyNotify) {
  999.     if (mbPtr->tkwin != NULL) {
  1000.         mbPtr->tkwin = NULL;
  1001.         Tcl_DeleteCommand(mbPtr->interp,
  1002.             Tcl_GetCommandName(mbPtr->interp, mbPtr->widgetCmd));
  1003.     }
  1004.     if (mbPtr->flags & REDRAW_PENDING) {
  1005.         Tcl_CancelIdleCall(DisplayMenuButton, (ClientData) mbPtr);
  1006.     }
  1007.     Tcl_EventuallyFree((ClientData) mbPtr, DestroyMenuButton);
  1008.     } else if (eventPtr->type == FocusIn) {
  1009.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1010.         mbPtr->flags |= GOT_FOCUS;
  1011.         if (mbPtr->highlightWidth > 0) {
  1012.         goto redraw;
  1013.         }
  1014.     }
  1015.     } else if (eventPtr->type == FocusOut) {
  1016.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1017.         mbPtr->flags &= ~GOT_FOCUS;
  1018.         if (mbPtr->highlightWidth > 0) {
  1019.         goto redraw;
  1020.         }
  1021.     }
  1022.     }
  1023.     return;
  1024.  
  1025.     redraw:
  1026.     if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) {
  1027.     Tcl_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  1028.     mbPtr->flags |= REDRAW_PENDING;
  1029.     }
  1030. }
  1031.  
  1032. /*
  1033.  *----------------------------------------------------------------------
  1034.  *
  1035.  * MenuButtonCmdDeletedProc --
  1036.  *
  1037.  *    This procedure is invoked when a widget command is deleted.  If
  1038.  *    the widget isn't already in the process of being destroyed,
  1039.  *    this command destroys it.
  1040.  *
  1041.  * Results:
  1042.  *    None.
  1043.  *
  1044.  * Side effects:
  1045.  *    The widget is destroyed.
  1046.  *
  1047.  *----------------------------------------------------------------------
  1048.  */
  1049.  
  1050. static void
  1051. MenuButtonCmdDeletedProc(clientData)
  1052.     ClientData clientData;    /* Pointer to widget record for widget. */
  1053. {
  1054.     MenuButton *mbPtr = (MenuButton *) clientData;
  1055.     Tk_Window tkwin = mbPtr->tkwin;
  1056.  
  1057.     /*
  1058.      * This procedure could be invoked either because the window was
  1059.      * destroyed and the command was then deleted (in which case tkwin
  1060.      * is NULL) or because the command was deleted, and then this procedure
  1061.      * destroys the widget.
  1062.      */
  1063.  
  1064.     if (tkwin != NULL) {
  1065.     mbPtr->tkwin = NULL;
  1066.     Tk_DestroyWindow(tkwin);
  1067.     }
  1068. }
  1069.  
  1070. /*
  1071.  *----------------------------------------------------------------------
  1072.  *
  1073.  * ComputeMenuButtonGeometry --
  1074.  *
  1075.  *    After changes in a menu button's text or bitmap, this procedure
  1076.  *    recomputes the menu button's geometry and passes this information
  1077.  *    along to the geometry manager for the window.
  1078.  *
  1079.  * Results:
  1080.  *    None.
  1081.  *
  1082.  * Side effects:
  1083.  *    The menu button's window may change size.
  1084.  *
  1085.  *----------------------------------------------------------------------
  1086.  */
  1087.  
  1088. static void
  1089. ComputeMenuButtonGeometry(mbPtr)
  1090.     register MenuButton *mbPtr;        /* Widget record for menu button. */
  1091. {
  1092.     int width, height, mm, pixels;
  1093.  
  1094.     mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth;
  1095.     if (mbPtr->image != None) {
  1096.     Tk_SizeOfImage(mbPtr->image, &width, &height);
  1097.     if (mbPtr->width > 0) {
  1098.         width = mbPtr->width;
  1099.     }
  1100.     if (mbPtr->height > 0) {
  1101.         height = mbPtr->height;
  1102.     }
  1103.     } else if (mbPtr->bitmap != None) {
  1104.     Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
  1105.     if (mbPtr->width > 0) {
  1106.         width = mbPtr->width;
  1107.     }
  1108.     if (mbPtr->height > 0) {
  1109.         height = mbPtr->height;
  1110.     }
  1111.     } else {
  1112.     mbPtr->numChars = strlen(mbPtr->text);
  1113.     TkComputeTextGeometry(mbPtr->fontPtr, mbPtr->text,
  1114.         mbPtr->numChars, mbPtr->wrapLength, &mbPtr->textWidth,
  1115.         &mbPtr->textHeight);
  1116.     width = mbPtr->textWidth;
  1117.     height = mbPtr->textHeight;
  1118.     if (mbPtr->width > 0) {
  1119.         width = mbPtr->width * XTextWidth(mbPtr->fontPtr, "0", 1);
  1120.     }
  1121.     if (mbPtr->height > 0) {
  1122.         height = mbPtr->height * (mbPtr->fontPtr->ascent
  1123.             + mbPtr->fontPtr->descent);
  1124.     }
  1125.     width += 2*mbPtr->padX;
  1126.     height += 2*mbPtr->padY;
  1127.     }
  1128.  
  1129.     if (mbPtr->indicatorOn) {
  1130.     mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin));
  1131.     pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin));
  1132.     mbPtr->indicatorHeight= (INDICATOR_HEIGHT * pixels)/(10*mm);
  1133.     mbPtr->indicatorWidth = (INDICATOR_WIDTH * pixels)/(10*mm)
  1134.         + 2*mbPtr->indicatorHeight;
  1135.     width += mbPtr->indicatorWidth;
  1136.     } else {
  1137.     mbPtr->indicatorHeight = 0;
  1138.     mbPtr->indicatorWidth = 0;
  1139.     }
  1140.  
  1141.     Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->inset),
  1142.         (int) (height + 2*mbPtr->inset));
  1143.     Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset);
  1144. }
  1145.  
  1146. /*
  1147.  *--------------------------------------------------------------
  1148.  *
  1149.  * MenuButtonTextVarProc --
  1150.  *
  1151.  *    This procedure is invoked when someone changes the variable
  1152.  *    whose contents are to be displayed in a menu button.
  1153.  *
  1154.  * Results:
  1155.  *    NULL is always returned.
  1156.  *
  1157.  * Side effects:
  1158.  *    The text displayed in the menu button will change to match the
  1159.  *    variable.
  1160.  *
  1161.  *--------------------------------------------------------------
  1162.  */
  1163.  
  1164.     /* ARGSUSED */
  1165. static char *
  1166. MenuButtonTextVarProc(clientData, interp, name1, name2, flags)
  1167.     ClientData clientData;    /* Information about button. */
  1168.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  1169.     char *name1;        /* Name of variable. */
  1170.     char *name2;        /* Second part of variable name. */
  1171.     int flags;            /* Information about what happened. */
  1172. {
  1173.     register MenuButton *mbPtr = (MenuButton *) clientData;
  1174.     char *value;
  1175.  
  1176.     /*
  1177.      * If the variable is unset, then immediately recreate it unless
  1178.      * the whole interpreter is going away.
  1179.      */
  1180.  
  1181.     if (flags & TCL_TRACE_UNSETS) {
  1182.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1183.         Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
  1184.             TCL_GLOBAL_ONLY);
  1185.         Tcl_TraceVar(interp, mbPtr->textVarName,
  1186.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1187.             MenuButtonTextVarProc, clientData);
  1188.     }
  1189.     return (char *) NULL;
  1190.     }
  1191.  
  1192.     value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
  1193.     if (value == NULL) {
  1194.     value = "";
  1195.     }
  1196.     if (mbPtr->text != NULL) {
  1197.     ckfree(mbPtr->text);
  1198.     }
  1199.     mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
  1200.     strcpy(mbPtr->text, value);
  1201.     ComputeMenuButtonGeometry(mbPtr);
  1202.  
  1203.     if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin)
  1204.         && !(mbPtr->flags & REDRAW_PENDING)) {
  1205.     Tcl_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  1206.     mbPtr->flags |= REDRAW_PENDING;
  1207.     }
  1208.     return (char *) NULL;
  1209. }
  1210.  
  1211. /*
  1212.  *----------------------------------------------------------------------
  1213.  *
  1214.  * MenuButtonImageProc --
  1215.  *
  1216.  *    This procedure is invoked by the image code whenever the manager
  1217.  *    for an image does something that affects the size of contents
  1218.  *    of an image displayed in a button.
  1219.  *
  1220.  * Results:
  1221.  *    None.
  1222.  *
  1223.  * Side effects:
  1224.  *    Arranges for the button to get redisplayed.
  1225.  *
  1226.  *----------------------------------------------------------------------
  1227.  */
  1228.  
  1229. static void
  1230. MenuButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
  1231.     ClientData clientData;        /* Pointer to widget record. */
  1232.     int x, y;                /* Upper left pixel (within image)
  1233.                      * that must be redisplayed. */
  1234.     int width, height;            /* Dimensions of area to redisplay
  1235.                      * (may be <= 0). */
  1236.     int imgWidth, imgHeight;        /* New dimensions of image. */
  1237. {
  1238.     register MenuButton *mbPtr = (MenuButton *) clientData;
  1239.  
  1240.     if (mbPtr->tkwin != NULL) {
  1241.     ComputeMenuButtonGeometry(mbPtr);
  1242.     if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
  1243.         Tcl_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
  1244.         mbPtr->flags |= REDRAW_PENDING;
  1245.     }
  1246.     }
  1247. }
  1248.